package com.netty.websocket.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.TimeUnit;

/**
* @ClassName: WebSocketServer
* @Description: WebSocketServer
* @author yuyehong@cloudwalk.cn
* @date 2020年4月29日 上午1:22:32
* @version v1.3.0
* @since 1.8
*/
@Component
@Slf4j
public class WebSocketServer {
	/**
	 * webSocket协议名
	 */
	private static final String WEBSOCKET_PROTOCOL = "WebSocket";

	/**
	 * 端口号
	 */
	@Value("${netty.server.port:9005}")
	private int port;
	
	@Value("${netty.server.path:/home}")
	private String path;

	private EventLoopGroup bossGroup;
	private EventLoopGroup workGroup;

	/**
	 * 启动
	 * @throws InterruptedException
	 */
	private void start() throws InterruptedException {
		// bossGroup就是parentGroup，是负责处理TCP/IP连接的
		bossGroup = new NioEventLoopGroup();
		// workerGroup就是childGroup,是负责处理Channel(通道)的I/O事件
		workGroup = new NioEventLoopGroup();

		ServerBootstrap sb = new ServerBootstrap();
		sb.option(ChannelOption.SO_BACKLOG, 1024);
		sb.group(workGroup, bossGroup).channel(NioServerSocketChannel.class).localAddress(this.port)
				.childHandler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel ch) {
						log.info("收到新连接：" + ch.localAddress());
						// HttpServerCodec：将请求和应答消息解码为HTTP消息
						ch.pipeline().addLast(new HttpServerCodec());
						// ChunkedWriteHandler：向客户端发送HTML5文件
						ch.pipeline().addLast(new ChunkedWriteHandler());
						// HttpObjectAggregator：将HTTP消息的多个部分合成一条完整的HTTP消息
						ch.pipeline().addLast(new HttpObjectAggregator(8192));
						ch.pipeline().addLast(new WebSocketServerProtocolHandler(path, WEBSOCKET_PROTOCOL, true, 65536 * 10));
						// 进行设置心跳检测
						ch.pipeline().addLast(new IdleStateHandler(60, 30, 60 * 30, TimeUnit.SECONDS));
						// 配置通道处理 来进行业务处理
						ch.pipeline().addLast(new WebSocketHandler());
					}
				});

		// 配置完成，开始绑定server，通过调用sync同步方法阻塞直到绑定成功
		ChannelFuture channelFuture = sb.bind(port).sync();
		log.info("【Netty服务启动成功========端口：" + port + "】");
		log.info("Server started and listen on:{}", channelFuture.channel().localAddress());
		// 成功绑定到端口之后,给channel增加一个 管道关闭的监听器并同步阻塞,直到channel关闭,线程才会往下执行,结束进程。
		channelFuture.channel().closeFuture().sync();

	}

	/**
	 * 释放资源
	 * @throws InterruptedException
	 */
	@PreDestroy
	public void destroy() throws InterruptedException {
		if (bossGroup != null) {
			bossGroup.shutdownGracefully().sync();
		}
		if (workGroup != null) {
			workGroup.shutdownGracefully().sync();
		}
	}

	@PostConstruct()
	public void init() {
		// 需要开启一个新的线程来执行netty server 服务器
		new Thread(() -> {
			try {
				start();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}).start();

	}
}
